﻿#include <QDir>
#include <QFileInfo>
#include <QDebug>
#include "fileSystemWatcher.h"


FileSystemWatcher* s_FileSystemWatcher = nullptr;
void FileSystemWatcher::setInstance(FileSystemWatcher * watcher)
{
    s_FileSystemWatcher = watcher;
}

FileSystemWatcher * FileSystemWatcher::getInstance(void)
{
    return s_FileSystemWatcher;
}

FileSystemWatcher::FileSystemWatcher(QObject *parent)
    : QObject(parent)
{
    m_pSystemWatcher = new QFileSystemWatcher();
    m_timerForReAdd = new QTimer(this);

    // 连接QFileSystemWatcher的directoryChanged和fileChanged信号到相应的槽
    connect(m_pSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryUpdated(QString)));
    connect(m_pSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(fileUpdated(QString)));
    connect(m_timerForReAdd, &QTimer::timeout, this, &FileSystemWatcher::timeoutAndReAdd);
    FileSystemWatcher::setInstance(this);
}

void FileSystemWatcher::timeoutAndReAdd(void)
{
    m_timerForReAdd->stop();
    foreach(auto path, pendingPathsToAdd)
    {
        this->addWatchPath(path);
        pendingPathsToAdd.removeOne(path);
        qDebug() << "timeoutAndReAdd :" << path;
    }
}

// 监控文件或目录
void FileSystemWatcher::addWatchPath(QString &path)
{
    if(isPathInWatch(path))
    {
        return ;
    }
    qDebug() << QString("FileSystemWatcher : Add to watch: %1").arg(path);

    QFileInfo file(path);
    if (file.isDir())
    {
        // 如果添加路径是一个目录，保存当前内容列表
        const QDir dirw(path);
        m_currentContentsMap[path] = dirw.entryList(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files, QDir::DirsFirst);
        // 添加监控路径
        m_pSystemWatcher->addPath(path);
        addedPaths.append(path);
    }else if (file.isFile())
    {
        // 添加监控路径
        m_pSystemWatcher->addPath(path);
        addedPaths.append(path);
    }else {
        qDebug() << "Try addWatchPath but not file" << path;
    }
}

void FileSystemWatcher::delWatchPath(QString &path)
{
    if(!isPathInWatch(path))
    {
        return ;
    }
    qDebug() << QString("FileSystemWatcher : Stop to watch: %1").arg(path);
    m_pSystemWatcher->removePath(path);
    addedPaths.removeOne(path);

    QFileInfo file(path);
    if (file.isDir())
    {
        m_currentContentsMap[path].clear();
    }
}

bool FileSystemWatcher::isPathInWatch(QString &path)
{
    foreach(auto item, addedPaths)
    {
        if(path == item)
        {
            return true;
        }
    }
    return false;
}

void FileSystemWatcher::stopWatchPathForAWhile(QString &path, int mseconds)
{
    this->delWatchPath(path);
    pendingPathsToAdd.append(path);
    m_timerForReAdd->stop();
    m_timerForReAdd->start(mseconds);
}

// 只要任何监控的目录更新（添加、删除、重命名），就会调用。
void FileSystemWatcher::directoryUpdated(const QString &path)
{
    qDebug() << QString("Directory updated: %1").arg(path);

    // 比较最新的内容和保存的内容找出区别(变化)
    QStringList currEntryList = m_currentContentsMap[path];
    const QDir dir(path);

    QStringList newEntryList = dir.entryList(QDir::NoDotAndDotDot  | QDir::AllDirs | QDir::Files, QDir::DirsFirst);

    QSet<QString> newDirSet = QSet<QString>::fromList(newEntryList);
    QSet<QString> currentDirSet = QSet<QString>::fromList(currEntryList);

    // 添加了文件
    QSet<QString> newFiles = newDirSet - currentDirSet;
    QStringList newFile = newFiles.toList();

    // 文件已被移除
    QSet<QString> deletedFiles = currentDirSet - newDirSet;
    QStringList deleteFile = deletedFiles.toList();

    // 更新当前设置
    m_currentContentsMap[path] = newEntryList;

    if (!newFile.isEmpty() && !deleteFile.isEmpty())
    {
        // 文件/目录重命名
        if ((newFile.count() == 1) && (deleteFile.count() == 1))
        {
            qDebug() << QString("File Renamed from %1 to %2").arg(deleteFile.first()).arg(newFile.first());
            if(!newFile.first().endsWith(".tmp"))
            {
                fileUpdated(newFile.first());
            }
        }
    }
    else
    {
        // 添加新文件/目录至Dir
        if (!newFile.isEmpty())
        {
            qDebug() << "new Files/Dirs added: " << newFile;

            foreach (QString file, newFile)
            {
                // 处理操作每个新文件....
                if(file.endsWith(".tmp"))
                    continue;
                fileUpdated(file);
            }
        }

        // 从Dir中删除文件/目录
        if (!deleteFile.isEmpty())
        {
            qDebug() << "Files/Dirs deleted: " << deleteFile;

            foreach(QString file, deleteFile)
            {
                // 处理操作每个被删除的文件....
                if(file.endsWith(".tmp"))
                    continue;
                fileUpdated(file);
            }
        }
    }
}

// 文件修改时调用
void FileSystemWatcher::fileUpdated(const QString &path)
{
    QFileInfo file(path);
    QString strPath = file.absolutePath();
    QString strName = file.fileName();

    qDebug() << QString("The file %1 at path %2 is updated").arg(strName).arg(strPath);
    qDebug() << "Update"  << path;
    emit NotifyConfigFileUpdated(path);
}


//按行读文本，处理以后重新写回，期间不触发警报
void FileSystemWatcher::PruneFileWithoutWatch(QString ReadFilePath)
{
    if(isPathInWatch(ReadFilePath))//mTopConfigFile))
    {
        stopWatchPathForAWhile(ReadFilePath, 2000);
    }
    QByteArray all;
    QByteArray line;
    QFile file(ReadFilePath);
    if(file.open(QIODevice::ReadWrite|QIODevice::Text))
    {
        while(!file.atEnd())
        {
            line = file.readLine();
            QString str(line);
            str.replace(QString('\\'), QString('/'));
            all += line.fromStdString(str.toStdString());
            //qDebug() << str;
        }
        file.close();
    }
    if(file.open(QIODevice::WriteOnly | QIODevice::Truncate))
    {
        file.write(all);
        file.close();
    }
}
